#include "func.h"
#include "ThreadDS.h"
#include "Comet.h"

static int cnt = 0;

void timeout (void* pds) {
	ThreadDS* ds = (ThreadDS*)pds;
	User* user = ds->user;
	Comet* comet = ds->comet;
	delete ds;
	ds = 0;
	pds = 0;

	string username = user->getUsername();
	unsigned fdcnt = user->getFdcnt();
#ifdef WIN32
	Sleep(Comet::getTimeoutMillSec());
#else
	usleep(Comet::getTimeoutMillSec() * 1000);
#endif
	if(comet->getUser(username) && fdcnt == user->getFdcnt())
		user->close();
}

void regHandle(ThreadDS* pds) {
	DWORD tid;
	THREADFUNC func = (THREADFUNC)timeout;
#ifdef WIN32
	HANDLE handle;
	handle = CreateThread(0, 0, func, pds, CREATE_SUSPENDED, &tid);
	ResumeThread(handle);
#else
	pthread_create(&tid, NULL, func, pds);
#endif
}
void handleClient(void* pds) {
	ThreadDS* ds = (ThreadDS*)pds;
	Comet* comet = ds->comet;
	SOCKET fd = ds->fd;
	delete ds;
	ds = 0;
	pds = 0;

	cout << "---- new connection ----" << ++cnt << endl;
	const int BUFSZ = 1024;
	char recvbuf[BUFSZ];
	int recvlen;

	// get data length, 4 bytes (int)
	for(int i = 0; i != 4; ++i) {
		if((recvlen = recv(fd, recvbuf + i, 1, 0)) == -1) {
			cerr << "recv datasz error" << endl;
			exit(1);
		}
	}
	int datasz = bytes2int(recvbuf);

	cout << "get data size: " << datasz << endl;

	// get data
	string data;
	while(datasz > 0) {
		if((recvlen = recv(fd, recvbuf, BUFSZ, 0)) == -1) {
			cerr << "recv data error" << endl;
			exit(1);
		}
		data += string(recvbuf, recvbuf + recvlen);
		datasz -= recvlen;
	}
	cout << "get data: " << data << endl;

	vector<string> blocks;
	unpackage(data, blocks);

	string msgType = blocks[0];
	
	if(msgType == Comet::MSGTYPE_RECV) {
		if(blocks.size() != 2) {
			cerr << "recv block should be 2 (" << blocks.size() << ")" << endl;
			exit(1);
		}
		string user = blocks[1];
		comet->addUser(user);
		comet->getUser(user)->setFd(fd);
		return ;
	}
	if(msgType == Comet::MSGTYPE_SEND) {
		if(blocks.size() != 3) {
			cerr << "send block should be 3 (" << blocks.size() << ")" << endl;
			exit(1);
		}
		string user = blocks[1];
		comet->addUser(user);
		comet->getUser(user)->addmsg(blocks[2]);
#ifdef WIN32
		closesocket(fd);
#else
		close(fd);
#endif
		return ;
	}
	if(msgType == Comet::MSGTYPE_DEL) {
		if(blocks.size() != 2) {
			cerr << "del block should be 2 (" << blocks.size() << ")" << endl;
			exit(1);
		}
		string user = blocks[1];
		comet->delUser(user);
#ifdef WIN32
		closesocket(fd);
#else
		close(fd);
#endif
		return ;
	}

}

int bytes2int(const char* bytes) {
	int val = 0;
	for(int i = 0; i != 4; ++i) {
		val += (unsigned char)(*(bytes + i)) << (i * 8);
	}
	return val;
}

bool unpackage(const string& data, vector<string>& blocks) {
	if(data.size() < 4) {
		cerr << "data format error" << endl;
		return false;
	}
	int blockCnt = bytes2int(data.c_str());
	if(blockCnt < 0) {
		cerr << "block count error, blockCnt = " << blockCnt << endl;
		return false;
	}

	unsigned int idx = 4;	// 4 bytes for blockSize.
	for(int i = 0; i != blockCnt; ++i) {
		int blockSize = bytes2int(data.c_str() + idx);
		if(blockSize < 0) {
			cerr << "block size error, blockSize = " << blockSize << endl;
			return false;
		}
		idx += 4;	// skip block size

		if(data.size() < idx + blockSize) {
			cerr << "data is uncompleted idx: " << idx << ", blockSize: " << blockSize << endl;
			return false;
		}

		blocks.push_back(string(data.c_str() + idx, data.c_str() + idx + blockSize));
		idx += blockSize;	// finish this block
	}

	return true;
}
